﻿using System;
using System.Linq;
using System.Management;
using System.Net.NetworkInformation;
using System.Security.Cryptography;
using System.Text;
using System.Runtime.InteropServices; // 用于检测虚拟化环境
using System.Collections.Generic;

namespace Mbs.Utils
{
    /// <summary>
    /// Utility class for generating a hardware-based machine code.
    /// </summary>
    public static class HardwareCode
    {
        private static readonly string _machineCode; // 静态只读字段，避免重复计算

        static HardwareCode() // 静态构造函数，在类加载时执行一次
        {
            _machineCode = ComputeMachineCode();
        }


        /// <summary>
        /// Gets the hardware-based machine code.  This value is computed once
        /// when the class is first accessed and then cached.
        /// </summary>
        public static string machineCode
        {
            get { return _machineCode; }
        }


        /// <summary>
        /// Computes the machine code by combining various hardware identifiers and hashing the result.
        /// </summary>
        /// <returns>A SHA256 hash representing the machine code.</returns>
        /// <summary>
        /// Computes the machine code by combining various hardware identifiers and hashing the result.
        /// </summary>
        /// <returns>A SHA256 hash representing the machine code.</returns>
        private static string ComputeMachineCode()
        {
            var hardwareIdentifiers = new List<string>
            {
                "AutoOrder",
                GetFirstCpuInfo(),
                GetMainBoardSerialNumber(),
                //GetHardDiskSerialNumber(),
                GetBiosSerialNumber(),
                //GetAllMacAddressesCombined(),
                GetFirstWiredMacAddress()
            };

            // Remove empty strings and trim whitespace from each identifier.  Also remove spaces
            var cleanedIdentifiers = hardwareIdentifiers
                .Where(s => !string.IsNullOrEmpty(s))
                .Select(s => s.Replace(" ", "").Trim());

            string combinedHardwareInfo = string.Join("|", cleanedIdentifiers);

            return HashString(combinedHardwareInfo);
        }




        /// <summary>
        /// Computes the SHA256 hash of the input string. Removes whitespace and trims the input before hashing.
        /// </summary>
        /// <param name="input">The string to hash.</param>
        /// <returns>The SHA256 hash as a hexadecimal string.</returns>
        private static string HashString(string input)
        {
            if (string.IsNullOrEmpty(input))
            {
                return string.Empty; // Or throw an exception, depending on desired behavior
            }

            using (SHA256 sha256Hash = SHA256.Create())
            {
                byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(input.Trim().Replace(" ", "")));
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < bytes.Length; i++)
                {
                    builder.Append(bytes[i].ToString("x2"));
                }
                return builder.ToString();
            }
        }

        /// <summary>
        /// Gets the processor ID of the first CPU found.
        /// </summary>
        /// <returns>The processor ID, or an empty string if an error occurs.</returns>
        private static string GetFirstCpuInfo()
        {
            try
            {
                using (ManagementClass mc = new ManagementClass("Win32_Processor"))
                using (ManagementObjectCollection moc = mc.GetInstances())
                {
                    foreach (ManagementObject mo in moc)
                    {
                        return mo["ProcessorId"]?.ToString() ?? string.Empty;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error getting CPU info: {ex.Message}"); // Log the exception
                return string.Empty;
            }
            return string.Empty;
        }

        /// <summary>
        /// Gets the serial number of the main board.
        /// </summary>
        /// <returns>The serial number, or an empty string if an error occurs.</returns>
        private static string GetMainBoardSerialNumber()
        {
            try
            {
                using (ManagementClass mc = new ManagementClass("Win32_BaseBoard"))
                using (ManagementObjectCollection moc = mc.GetInstances())
                {
                    foreach (ManagementObject mo in moc)
                    {
                        return mo["SerialNumber"]?.ToString() ?? string.Empty;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error getting mainboard serial number: {ex.Message}"); // Log the exception
                return string.Empty;
            }
            return string.Empty;
        }

        /// <summary>
        /// Gets the serial number of the first physical media (hard disk) found.
        /// </summary>
        /// <returns>The serial number, or an empty string if an error occurs.</returns>
        private static string GetHardDiskSerialNumber()
        {
            try
            {
                using (ManagementClass mc = new ManagementClass("Win32_PhysicalMedia"))
                using (ManagementObjectCollection moc = mc.GetInstances())
                {
                    foreach (ManagementObject mo in moc)
                    {
                        return mo["SerialNumber"]?.ToString() ?? string.Empty;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error getting hard disk serial number: {ex.Message}"); // Log the exception
                return string.Empty;
            }
            return string.Empty;
        }

        /// <summary>
        /// Gets the serial number of the BIOS.
        /// </summary>
        /// <returns>The serial number, or an empty string if an error occurs.</returns>
        private static string GetBiosSerialNumber()
        {
            try
            {
                using (ManagementClass mc = new ManagementClass("Win32_BIOS"))
                using (ManagementObjectCollection moc = mc.GetInstances())
                {
                    foreach (ManagementObject mo in moc)
                    {
                        return mo["SerialNumber"]?.ToString() ?? string.Empty;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error getting BIOS serial number: {ex.Message}"); // Log the exception
                return string.Empty;
            }
            return string.Empty;
        }


        /// <summary>
        /// Gets all MAC addresses combined into a single string.
        /// </summary>
        /// <returns>A string containing all MAC addresses, or an empty string if none are found.</returns>
        private static string GetAllMacAddressesCombined()
        {
            try
            {
                NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
                StringBuilder macAddresses = new StringBuilder();
                foreach (NetworkInterface networkInterface in networkInterfaces)
                {
                    macAddresses.Append(networkInterface.GetPhysicalAddress().ToString());
                }
                return macAddresses.ToString();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error getting all MAC addresses: {ex.Message}");
                return string.Empty;
            }
        }

        /// <summary>
        /// Gets the MAC address of the first wired network adapter.
        /// </summary>
        /// <returns>The MAC address, or "0000" if an error occurs or no wired adapter is found.</returns>
        private static string GetFirstWiredMacAddress()
        {
            try
            {
                NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

                // LINQ to find the first wired network interface (Ethernet)
                NetworkInterface wiredInterface = networkInterfaces.FirstOrDefault(ni => ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet);


                if (wiredInterface != null)
                {
                    PhysicalAddress pa = wiredInterface.GetPhysicalAddress();
                    byte[] macBytes = pa.GetAddressBytes();
                    return BitConverter.ToString(macBytes).Replace("-", ":").ToUpper();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error getting first wired MAC address: {ex.Message}");
            }
            return "0000";
        }
    }
}
